# 
#   create_makefile.rb
# 
#   Copyright (c) 2010-2011 Masoom Shaikh<masoomshaikh@users.sourceforge.net>
#   All rights reserved.
# 
#   Redistribution and use in source and binary forms, with or without
#   modification, are permitted provided that the following conditions
#   are met:
#   1. Redistributions of source code must retain the above copyright
#      notice, this list of conditions and the following disclaimer.
#   2. Redistributions in binary form must reproduce the above copyright
#      notice, this list of conditions and the following disclaimer in the
#      documentation and/or other materials provided with the distribution.
# 
#   THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
#   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#   ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
#   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
#   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
#   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
#   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
#   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
#   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#   SUCH DAMAGE.

$: << File.join( File.dirname( __FILE__))

require 'ostruct'
require 'optparse'
require 'msxml'

def read_arguments( options, args)
  begin
    opts = OptionParser.new do |opts|
      opts.banner = "#{__FILE__} options"

      opts.separator ""

      opts.on( "-l", "--list-confs", "list all available configuration") do |arg|
        options.list = true
      end

      opts.on( "-c", "--conf CONF", "build configuration") do |arg|
        options.conf_name = arg.to_s
      end

      opts.on( "-p", "--platform [PLATFORM]", "platform to build") do |arg|
        options.platform = arg.to_s
      end

      opts.on_tail( "-h", "--help", "show this help screen") do
        puts opts
        exit
      end
    end.parse!(args)
    options.project = args[0]
  rescue OptionParser::ParseError => err
    puts err
    exit
  end
end

def c_or_cpp_file?( file)
  cxx_files = %W( .cpp .c)
  cxx_files.include? File.extname( file)
end

class File
  def self.sub_ext( file, ext)
    file.sub( File.extname( file), ext)
  end
end

def create_makefile( options, mkfile)
  mkfile << "CC = cl.exe -nologo\n"
  mkfile << "CFLAGS = -c -O2 -GL -W3 -TP -EHsc -MT\n"
  mkfile << "LD = link.exe -nologo\n"
  mkfile << "LDFLAGS = -DLL -SUBSYSTEM:WINDOWS -OPT:REF -OPT:ICF -LTCG -MACHINE:X86\n\n"

  proj = VCProj.new( options.project)
  begin
    conf = proj.get_configuration( options.conf_name, options.platform)
  rescue StandardError => err
    puts err.message
    exit
  end

  # constants
  mkfile << "PlatformName      = " << conf.platform         << "\n"
  mkfile << "ConfigurationName = " << conf.conf_name        << "\n"
  mkfile << "ProjectName       = " << proj.name             << "\n"
  mkfile << "OutDir            = " << conf.output_directory << "\n"
  mkfile << "\n"

  # pre-processor directives
  mkfile << "DIRECTIVES = "
  mkfile << " \\\n\t-DUNICODE"
  mkfile << " \\\n\t-D_UNICODE"
  mkfile << conf.preprocessor_directives.join( " \\\n\t-D")
  mkfile << "\n\n"

  # C/C++ include folders
  mkfile << "INCLUDES = -I"
  mkfile << conf.include_directories.join( " \\\n\t-I")
  mkfile << "\n\n"

  # linker libpath
  mkfile << "LIBPATH = -LIBPATH:"
  mkfile << conf.lib_directories.join( " \\\n\t-LIBPATH:")
  mkfile << "\n\n"

  # required libs
  mkfile << "LIBS = "
  mkfile << conf.lib_dependencies.join( " \\\n\t")
  mkfile << "\n\n"

  # derive C\C++ filenames's obj names
  obj_files = proj.all_files.collect do |file|
    next unless c_or_cpp_file? file
    "$(OutDir)\\$(ProjectName)\\" + File.sub_ext( File.basename( file), ".obj")
  end
  obj_files.reject! do |file| file.nil? end

  mkfile << "OBJ_FILES = " << obj_files.join(" \\\n\t") << "\n\n"

  # targets
  main_target = "$(OutDir)\\$(ProjectName).dll"
  mkfile << main_target << ": check_dir $(OBJ_FILES)\n"
  mkfile << "\t$(LD) $(LDFLAGS) $(LIBPATH) $(LIBS) -OUT:$@ $(OBJ_FILES)"
  mkfile << "\n\n"

  mkfile << "check_dir:\n"
	mkfile << "\t@IF NOT EXIST $(OutDir)\\$(ProjectName) MKDIR $(OutDir)\\$(ProjectName)\n\n"

  mkfile << "clean:\n"
  mkfile << "\t@del $(OutDir)\\$(ProjectName).*\n"
  mkfile << "\t@del $(OBJ_FILES)\n"
  mkfile << "\t@rd /s/q $(OutDir)\\$(ProjectName)\n\n"

  obj_files.each do |obj_file|
    mkfile << obj_file << ":\n"
    mkfile << "\t$(CC) $(DIRECTIVES) $(INCLUDES) $(CFLAGS) -Fo$@ ..\\src\\$(*B).cpp\n\n"
  end
end

options = OpenStruct.new
options.list = false
options.platform = "Win32"
options.conf_name = "Release"
options.project = ""
read_arguments( options, ARGV)

if options.list
  VCProj.new( options.project).configurations.each do |conf|
    puts conf.name
  end
  exit
end

File.open( File.sub_ext( options.project, '.mak'), 'w') do |mkfile|
  create_makefile( options, mkfile)
end
